package com.gao.f_reactor.d.server;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author gao
 * @time 2020/08/23 18:33:54
 */
public class SubReactor implements Runnable {

    private Selector selector;

    public SubReactor() {
        try {
            selector = Selector.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void register(SocketChannel channel) throws IOException {
        channel.configureBlocking(false);
        // 因为Selector的select方法和register方法是互斥的，在竞争相同的锁（通过源码追踪可以发现是：Selector.publicKeys）
        // 所以以下需要调用selector.wakeup()来让select()方法立即返回，这样的话，select()方法就会返回0
        selector.wakeup();
        SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
        key.attach(new Handler(channel, selector));
    }

    @Override
    public void run() {
        try {
            while (true) {
                int n = selector.select();
                if(n == 0) {
                    // 为了防止selector.select()被wakeup之后，又立即进入下一轮循环获得锁
                    // 进而继续使上面register方法中的channel.register阻塞，所以下面让当前
                    // 线程阻塞一小会儿。
                    Thread.yield();
                    Thread.sleep(10);
                    continue;
                }
                if (n > 0) {
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> it = selectedKeys.iterator();
                    while (it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        dispatch(key);
                    }
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void dispatch(SelectionKey key) {
        Runnable runnable = (Runnable) key.attachment();
        runnable.run();
    }
}
